home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Applications
/
Audio
/
Patchmix
/
Source
/
PatchView.m
< prev
next >
Wrap
Text File
|
1992-08-18
|
18KB
|
635 lines
// PatchView.m by Mara Helmuth
#import <math.h>
#import <appkit/Application.h>
#import <appkit/NXImage.h>
#import <appkit/graphics.h>
#import <appkit/Form.h>
#import <appkit/Panel.h>
#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
#import "PatchWindow.h"
#import "PatchView.h"
#import "draw.h"
#import "Oscil.h"
#import "Arith.h"
#import "Rand.h"
#import "Out.h"
#import "Evp.h"
#import "Converter.h"
#import "Buzz.h"
#import "Reson.h"
#import "Comb.h"
#import "Pluck.h"
#import "Allpass.h"
//#import "Delay.h"
//#import "Reverb.h"
//#import "WS.h"
#import "Instrum.h"
#import "Param.h"
@implementation PatchView
- initFrame:(const NXRect *)frameRect
{
[super initFrame:frameRect];
/* off-screen buffer */
screenImage = [[NXImage alloc] initSize:&bounds.size];
return self;
}
- setImages // erase the patchview and make ugen images
{
[self erase:0];
connecting = NO;
moving = NO;
setting = NO;
PSInit(); // initialize postscript drawing defs
return self;
}
/* instance methods */
- windowChanged:newWindow
{
NXRect rect;
[super windowChanged:newWindow];
/* if our new window's an PatchWindow, we'll "register" ourself with it */
if ([newWindow respondsTo:@selector(registerRect:forView:)]) {
/* give the window our window-based coordinates */
rect = bounds;
[self convertRect:&rect toView:nil];
[newWindow registerRect:&rect forView:self];
}
return self;
}
- setUgen:(int)uType
{
ugenType = uType;
return self;
}
- setDelegate:anObject
{
delegate = anObject;
return self;
}
- (BOOL)windowEntered:dragSource
{
/* let our window know we've done some drawing */
return YES;
}
- (BOOL)windowExited:dragSource
{
/* let our window know we've done some drawing */
return YES;
}
- (BOOL)windowDropped:dragSource:(NXPoint *)currentLocation
{
/* the user dropped a window into us */
[window flushWindow];
if ([delegate respondsTo:@selector(acceptedWindow:fromSource:)]) {
[delegate acceptedWindow:self fromSource:dragSource];
}
// put the new ugen into the UnitGen ugenList
switch(ugenType) {
case 5: {
currentUgen = [[Arith alloc] init];
[currentUgen setAtype:'+'];
break;
}
case 6: {
currentUgen = [[Arith alloc] init];
[currentUgen setAtype:'-'];
break;
}
case 7: {
currentUgen = [[Arith alloc] init];
[currentUgen setAtype:'*'];
break;
}
case 8: {
currentUgen = [[Arith alloc] init];
[currentUgen setAtype:'/'];
break;
}
/*
case 9: {
currentUgen = [[WS alloc] init];
break;
}
*/
case 10: {
currentUgen = [[Comb alloc] init];
break;
}
case 11: {
currentUgen = [[Pluck alloc] init];
break;
}
/* case 12: {
currentUgen = [[Allpass alloc] init];
break;
}
*/ case 13: {
currentUgen = [[Allpass alloc] init]; // delay actually?
break;
}
/* case 14: {
currentUgen = [[Reverb alloc] init];
break;
}
*/
case 15: {
currentUgen = [[Oscil alloc] init];
break;
}
case 16: {
currentUgen = [[Buzz alloc] init];
break;
}
case 17: {
currentUgen = [[Rand alloc] init];
break;
}
case 18: {
currentUgen = [[Evp alloc] init];
break;
}
case 19: {
currentUgen = [[Reson alloc] init];
break;
}
case 20:
case 21:
case 22:
case 23:
case 24:
case 25: {
currentUgen = [[Converter alloc] init];
[currentUgen setCtype:ugenType];
break;
}
default: {
NXRunAlertPanel("patchmix","Sorry, this unit generator is not yet working","OK",NULL,NULL);
return NO;
}
}
currentUgenCenter.x = currentUgenCenter.y = 40;
/* we accept it */
[self convertPoint:currentLocation fromView:nil];
// center on the cursor
currentLocation->x -= [currentUgen getCenter]->x;
currentLocation->y -= [currentUgen getCenter]->y;
[currentUgen move:currentLocation];
currentLocation = [currentUgen getLocation];
currentImage = [currentUgen getImage];
// draw the current ugen into the view
[self lockFocus];
[currentImage composite:NX_SOVER toPoint:currentLocation];
[self unlockFocus];
[window flushWindow];
// draw the current ugen into the off-screen buffer
if ([screenImage lockFocus]) {
[currentImage composite:NX_SOVER toPoint:currentLocation];
[screenImage unlockFocus];
}
[window disableFlushWindow];
[self display];
[window reenableFlushWindow];
return YES;
}
- drawSelf:(NXRect *)rects :(int)count
{
/* load the appropriate portion of the off-screen buffer
* into the visible portion of the view
*/
[screenImage composite:NX_COPY fromRect:rects toPoint:&(rects->origin)];
return self;
}
- erase:sender
// erase the view and free up all the unit generators
{
NXSize imageSize;
NXPoint outLocation;
setting = connecting = moving = NO;
if ([screenImage lockFocus]) {
NXEraseRect(&bounds);
[screenImage unlockFocus];
}
[Inst freeUgens]; // free up ugens in list
currentUgen = [[Out alloc] init];
// draw the out ugen into the view
currentImage = [currentUgen getImage];
[currentImage getSize:&imageSize];
outLocation.x = floor((NX_WIDTH(&bounds) - imageSize.width) / 2.0);
outLocation.y = floor((NX_HEIGHT(&bounds) - imageSize.height) / 5.0);
[currentUgen move:&outLocation];
[self lockFocus];
[currentImage composite:NX_SOVER toPoint:&outLocation];
[self unlockFocus];
[window flushWindow];
// draw the out ugen into the off-screen buffer
if ([screenImage lockFocus]) {
[currentImage composite:NX_SOVER toPoint:&outLocation];
[screenImage unlockFocus];
}
[self display];
return self;
}
- mouseDown:(NXEvent *)e
/* logic for mouse down events:
convert the point location
if a unit generator found at the mouse down point
if a parameter found at the mouse down point
if we are setting a parameter
set the parameter to whatever use input into Param form field
unhighlight the parameter
else if the event is a double click
we are setting a parameter so highlight it
diplay the parameter name and value in the Param form field
else
we are connecting unit generators so save the location
else (a unit generator found, but no parameter)
if event is a double-click
delete unit generator if user confirms and it is not an out ugen
else single-click
if setting, user error - display panel
else we are moving a unit generator - erase and redraw at location
and erase all its connections
else no unit generator found
if setting - user error - display panel
while event is not a mouseUp,
call mouseDraggedAction (for moving unit generator)
*/
{
id pList;
NXPoint startPoint = e->location, nextPoint;
int looping = YES, i;
int oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
/* do first mouse down action */
[self convertPoint:&startPoint fromView:nil];
//printf("mouseDown\n");
if(currentUgen = [Inst findUgenAtPoint:&startPoint]) {
currentImage = [currentUgen getImage];
if(currentParam = [currentUgen findParamAtPoint:&startPoint]) {
if(setting) { // mouse click after set param val
[currentParam setValue:[paramVal stringValueAt:0]];
setting = NO;
hrect = [currentParam getRect];
// unhighlight param
[self lockFocus];
NXHighlightRect(hrect);
[self unlockFocus];
[window flushWindow];
}
else if(e->data.mouse.click == 2) { // double click,
// begin to set param...
connecting = moving = NO;
setting = YES;
hrect = [currentParam getRect]; // highlight the param
[self lockFocus];
NXHighlightRect(hrect);
[self unlockFocus];
[window flushWindow];
[paramVal setStringValue:[currentParam getValue] at:0];
[paramVal setTitle:[currentParam getTitle] at:0];
[paramVal selectTextAt:0];
}
else { // no double click
connecting = YES;
moving = setting = NO;
connPoint1 = [currentParam getDrawPoint];
connUgen1 = currentUgen;
connParam1 = currentParam;
}
}
else { // no param found, but a ugen selected
if(e->data.mouse.click == 2) { // double click, delete ugen?
ugenselected = YES;
hrect = [currentUgen getRect]; // highlight the ugen
[self lockFocus];
NXHighlightRect(hrect);
[self unlockFocus];
[window flushWindow];
choice = NXRunAlertPanel("Cut Alert", "Delete this unit generator?", "Blow it away","No, Save it!",NULL);
hrect = [currentUgen getRect];
// unhighlight ugen
[self lockFocus];
NXHighlightRect(hrect);
[self unlockFocus];
[window flushWindow];
if(choice == NX_ALERTDEFAULT) {
if(!strcmp("Out",[currentUgen getType]))
NXRunAlertPanel("Cut Alert", "Can't delete Output Unit Generator!", "OK", NULL, NULL);
else {
if(![currentUgen remove]) {
NXRunAlertPanel("Cut Alert", "Can't delete a connected unit generator! Please delete connectors first.", "OK", NULL, NULL);
}
else {
//THIS ERASES A UGEN
// draw the current ugen into the view
[self lockFocus];
[[currentUgen getImage] composite:NX_XOR toPoint:[currentUgen getLocation]];
[self unlockFocus];
[window flushWindow];
// draw the current ugen into the off-screen buffer
if ([screenImage lockFocus]) {
[[currentUgen getImage] composite:NX_XOR toPoint:[currentUgen getLocation]];
[screenImage unlockFocus];
}
}
}
}
[self display];
}
else { // not a double-click, ugen selected
if(setting) { // unhighlight param and stop setting - user error
[self lockFocus];
NXHighlightRect(hrect);
[self unlockFocus];
[window flushWindow];
NXRunAlertPanel("Param Alert", "I hope you know you have to click on the param again to save the value...", "Oh, alright!", NULL, NULL);
setting = NO;
}
else { // unit gen selected no param, not double click, not setting
moving = YES;
connecting = setting = NO;
// erase the original so you can move image around
[self lockFocus];
[[currentUgen getImage] composite:NX_XOR toPoint:[currentUgen getLocation]];
[self unlockFocus];
[window flushWindow];
// draw the current ugen into the off-screen buffer
if ([screenImage lockFocus]) {
[[currentUgen getImage] composite:NX_XOR toPoint:[currentUgen getLocation]];
[screenImage unlockFocus];
}
// erase the connectors too, for now
pList = [currentUgen getParamList];
for(i = 0; i < [pList count]; i++) {
currentParam = [pList objectAt:i];
if(eraseParam = [currentParam getConnectedParam]) { // if connected
ePoint1 = [currentParam getDrawPoint];
ePoint2 = [eraseParam getDrawPoint];
[self lockFocus];
PSsetgray(NX_WHITE);
PSLine(ePoint1->x,ePoint1->y, ePoint2->x-ePoint1->x, ePoint2->y-ePoint1->y);
[self unlockFocus];
if ([screenImage lockFocus]) {
PSsetgray(NX_WHITE);
PSLine(ePoint1->x,ePoint1->y, ePoint2->x-ePoint1->x, ePoint2->y-ePoint1->y);
[screenImage unlockFocus];
}
}
}
// draw ugen where mouse down is and store points
nextPoint.x = startPoint.x - currentUgenCenter.x;
nextPoint.y = startPoint.y - currentUgenCenter.y;
// draw the current ugen into the view
[self lockFocus];
[currentImage composite:NX_SOVER toPoint:&nextPoint];
[self unlockFocus];
[window flushWindow];
if ([screenImage lockFocus]) {
[currentImage composite:NX_SOVER toPoint:&nextPoint];
[screenImage unlockFocus];
}
erasePoint1.x = nextPoint.x;
erasePoint1.y = nextPoint.y;
}
}
}
}
// no ugen found
else {
if(setting) { // unhighlight param and stop setting - user error
[self lockFocus];
NXHighlightRect(hrect);
[self unlockFocus];
[window flushWindow];
NXRunAlertPanel("Param Alert", "I hope you know you have to click on the param again to save the value...", "Oh, alright!", NULL, NULL);
setting = NO;
}
}
while (looping) {
e = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
nextPoint = e->location;
[self convertPoint:&nextPoint fromView:nil];
if(e->type == NX_MOUSEDRAGGED) {
[superview autoscroll:e];
/* TO DRAG AN IMAGE: */
if(moving) { // erase the old
[self lockFocus];
[currentImage composite:NX_XOR toPoint:&erasePoint1];
[self unlockFocus];
[window flushWindow];
// erase off-screen buffer, not needed if not drawing there
if ([screenImage lockFocus]) {
[currentImage composite:NX_XOR toPoint:&erasePoint1];
[screenImage unlockFocus];
}
// do continuing mouse dragged action
[self mouseDraggedAction:&nextPoint];
}
}
else { // mouse up - drag is done
looping = NO;
/* do mouse up action */
[self mouseUpAction:&nextPoint];
[window setEventMask:oldMask];
}
}
return self;
}
- mouseDraggedAction:(NXPoint *)currentLocation
{
// THIS DRAGS A UGEN: draws ugen on new points */
if (moving) {
currentLocation->x -= currentUgenCenter.x;
currentLocation->y -= currentUgenCenter.y;
// draw the current ugen into the view
[self lockFocus];
[currentImage composite:NX_SOVER toPoint:currentLocation];
[self unlockFocus];
[window flushWindow];
// draw the current ugen into the off-screen buffer
if ([screenImage lockFocus]) {
[currentImage composite:NX_SOVER toPoint:currentLocation];
[screenImage unlockFocus];
}
erasePoint1.x = currentLocation->x;
erasePoint1.y = currentLocation->y;
}
return self;
}
- mouseUpAction:(NXPoint *)currentLocation
/* if moving
redraw the connections to other unit generators
else if connecting
for each parameter:
erase previous connections
draw the new connection
store the new connections
*/
{
id pList;
BOOL draw = YES;
int i;
if(moving) {
// for each param, redraw if connected
// save new location for Ugen
currentLocation->x -= currentUgenCenter.x;
currentLocation->y -= currentUgenCenter.y;
[currentUgen move:currentLocation];
// if ugen connected, redraw connectors
// for each param, check if connected, redraw line
pList = [currentUgen getParamList];
for(i = 0; i < [pList count]; i++) {
currentParam = [pList objectAt:i];
if(connParam1 = [currentParam getConnectedParam]) { // if connected
connPoint1 = [currentParam getDrawPoint];
connPoint2 = [connParam1 getDrawPoint];
[self lockFocus];
PSLine(connPoint1->x,connPoint1->y, connPoint2->x-connPoint1->x, connPoint2->y-connPoint1->y);
[self unlockFocus];
if ([screenImage lockFocus]) {
PSLine(connPoint1->x,connPoint1->y, connPoint2->x-connPoint1->x, connPoint2->y-connPoint1->y);
[screenImage unlockFocus];
}
}
}
[self display];
moving = NO;
}
// if connecting draw final line
else if(connecting) {
if((currentUgen = [Inst findUgenAtPoint:currentLocation]) != connUgen1) {
// test if these connectors are already connected
// if so, erase the line
// erase any previous connections for either connector
if(currentParam = [currentUgen findParamAtPoint:currentLocation]) {
connPoint2 = [currentParam getDrawPoint];
if([connParam1 getConnectedParam] == currentParam) {
// they are already connected, ERASE
// and disconnect
//printf("already conn, erasing\n");
[connParam1 setConnectedParam:nil];
[currentParam setConnectedParam:nil];
[self lockFocus];
PSsetgray(NX_WHITE);
PSLine(connPoint1->x,connPoint1->y, connPoint2->x-connPoint1->x, connPoint2->y-connPoint1->y);
[self unlockFocus];
if ([screenImage lockFocus]) {
PSsetgray(NX_WHITE);
PSLine(connPoint1->x,connPoint1->y, connPoint2->x-connPoint1->x, connPoint2->y-connPoint1->y);
[screenImage unlockFocus];
}
draw = NO;
} // conn1 was connected, erase
else {
if(eraseParam = [connParam1 getConnectedParam]) {
ePoint2 = [eraseParam getDrawPoint];
[connParam1 setConnectedParam:nil];
[eraseParam setConnectedParam:nil];
[self lockFocus];
PSsetgray(NX_WHITE);
PSLine(connPoint1->x,connPoint1->y, ePoint2->x-connPoint1->x, ePoint2->y-connPoint1->y);
[self unlockFocus];
if ([screenImage lockFocus]) {
PSsetgray(NX_WHITE);
PSLine(connPoint1->x,connPoint1->y, ePoint2->x-connPoint1->x, ePoint2->y-connPoint1->y);
[screenImage unlockFocus];
}
} // conn2 was connected, erase
if(eraseParam = [currentParam getConnectedParam]) {
ePoint1 = [eraseParam getDrawPoint];
[currentParam setConnectedParam:nil];
[eraseParam setConnectedParam:nil];
[self lockFocus];
PSsetgray(NX_WHITE);
PSLine(ePoint1->x,ePoint1->y, connPoint2->x-ePoint1->x, connPoint2->y-ePoint1->y);
[self unlockFocus];
if ([screenImage lockFocus]) {
PSsetgray(NX_WHITE);
PSLine(ePoint1->x,ePoint1->y, connPoint2->x-ePoint1->x, connPoint2->y-ePoint1->y);
[screenImage unlockFocus];
}
}
}
if(draw) { // draw the connnector
[connParam1 setConnectedParam:currentParam];
[currentParam setConnectedParam:connParam1];
[self lockFocus];
PSLine(connPoint1->x,connPoint1->y, connPoint2->x-connPoint1->x, connPoint2->y-connPoint1->y);
[self unlockFocus];
if ([screenImage lockFocus]) {
PSLine(connPoint1->x,connPoint1->y, connPoint2->x-connPoint1->x, connPoint2->y-connPoint1->y);
[screenImage unlockFocus];
}
}
}
[self display];
connecting = NO;
}
}
return self;
}
/* delegation methods */
- acceptedWindow:acceptView fromSource:source
{
/*
* if delegate implements this method, it'll get called whenever the user
* drops a window into the view
*/
return self;
}
@end